﻿using System.IO;
using System.Net;
using Dimebrain.TweetSharp.Fluent;

namespace KlearTweet.Web
{
    [System.ServiceModel.ServiceContract(Namespace = "http://www.kleartweet.com")]
    [System.ServiceModel.Activation.AspNetCompatibilityRequirements(
        RequirementsMode = System.ServiceModel.Activation.AspNetCompatibilityRequirementsMode.Allowed)]
    public class Proxy
    {
        private static readonly System.Collections.Generic.List<string> RestrictedHeaders =
            new System.Collections.Generic.List<string> { "Accept", "Connection", "Content-Length", "Content-Type", "Date", "Expect",
                "Host", "If-Modified-Since", "Range", "Referer", "Transfer-Encoding", "User-Agent", "Proxy-Connection",
                "Set-Cookie" }; // Added to not break Silverlight


        [System.ServiceModel.OperationContract]
        [System.ServiceModel.Web.WebInvoke(UriTemplate = "*", Method = "*")]
        public Stream Post(Stream inputStream)
        {
            if (System.Diagnostics.Debugger.IsAttached) // ProcessRequest without try/catch
                return ProcessRequest(inputStream);
            try
            {
                return ProcessRequest(inputStream);
            }
            catch (System.Exception ex)
            {
                var error = EventLogger.Error("/Proxy.svc/Post/", ex);
                return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(error)); // TODO: Shouldn't return the error as-is!
            }
        }


        public Stream ProcessRequest(Stream inputStream)
        {
            var ctx = System.ServiceModel.Web.WebOperationContext.Current;
            if (ctx == null)
                throw new System.NotSupportedException();

            var baseUri = ctx.IncomingRequest.UriTemplateMatch.BaseUri;
            var requestUri = ctx.IncomingRequest.UriTemplateMatch.RequestUri;

            var incomingRequestHeaders = new System.Collections.Generic.Dictionary<string, string>();
            foreach (var header in ctx.IncomingRequest.Headers.AllKeys)
                incomingRequestHeaders[header] = ctx.IncomingRequest.Headers[header];

            ctx.OutgoingResponse.Headers["Cache-Control"] = "no-cache";
            ctx.OutgoingResponse.ContentType = "application/json"; // TODO: Can also be XML; detect using the url!


            string url;
            {
                url = requestUri.AbsoluteUri.Substring((baseUri.AbsoluteUri+"/Post").Length);
                var twitterUrl = url.StartsWith("/search.json") ? "http://search.twitter.com" : "http://twitter.com";
                url = twitterUrl + url;
            }

            if (url == "http://twitter.com/oauth/request_token") // TODO: Find a more elequant way to handle this
            {
                var requestToken = FluentTwitter.CreateRequest()
                    .Authentication.GetRequestToken(Storage.AccountsManager.ConsumerKey, Storage.AccountsManager.ConsumerSecret);

                var response = requestToken.Request();

                return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(response));
            }


            string stringToPost;
            using (var reader = new StreamReader(inputStream))
                stringToPost = reader.ReadToEnd();

            if (stringToPost.StartsWith(baseUri.AbsoluteUri))
                stringToPost = url;

            var req = (HttpWebRequest) WebRequest.Create(url);
            req.ServicePoint.Expect100Continue = false;
            bool isPost = true;
            string clientAuth = null;

            foreach (var header in incomingRequestHeaders.Keys)
            {
                switch (header)
                {
                    case "X-Twitter-Method":
                        isPost = incomingRequestHeaders[header] != "GET";
                        break;
                    case "X-Twitter-Agent":
                        req.Headers[header] = req.UserAgent = incomingRequestHeaders[header];
                        break;
                    case "X-Twitter-Client":
                    case "X-Twitter-Version":
                    case "X-Twitter-URL":
                        req.Headers[header] = incomingRequestHeaders[header];
                        break;
                    case "Referer":
                        req.Referer = incomingRequestHeaders[header];
                        break;
                    /*case "X-Twitter-Accept": // TODO: Should this be done?
                        req.Headers["Accept-Encoding"] = incomingRequestHeaders[header];
                        break;*/
                    case "X-Twitter-Auth":
                        clientAuth = incomingRequestHeaders[header];
                        break;
                }
            }

            if (!string.IsNullOrEmpty(clientAuth))
                req.Headers["Authorization"] = Storage.AccountsManager.GetAuthorization(clientAuth, isPost, url, stringToPost);

            if (string.IsNullOrEmpty(req.Referer) && !string.IsNullOrEmpty(req.Headers["X-Twitter-URL"]))
                req.Referer = req.Headers["X-Twitter-URL"];

            if (isPost && !string.IsNullOrEmpty(stringToPost))
            {
                var bytesToPost = System.Text.Encoding.UTF8.GetBytes(stringToPost);

                req.Method = "POST";
                req.ContentType = "application/x-www-form-urlencoded";
                req.ContentLength = bytesToPost.Length;

                using (var reqStream = req.GetRequestStream())
                    reqStream.Write(bytesToPost, 0, bytesToPost.Length);
            }

            using (var response = (HttpWebResponse) req.GetResponse())
            {
                if(response.StatusCode != HttpStatusCode.OK)
                    throw new System.Exception("Failed"); // TODO: Return a Twitter error? + Seems to never happen

                // Forward the headers
                foreach (var header in response.Headers.AllKeys)
                    if (!RestrictedHeaders.Contains(header))
                        ctx.OutgoingResponse.Headers[header] = response.Headers[header];

                var responseStream = response.GetResponseStream();

                // Make a copy
                var copyStream = new MemoryStream(System.Math.Max((int)response.ContentLength, 4));
                var buffer = new byte[2 * 1024];
                int bytesRead;
                while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
                    copyStream.Write(buffer, 0, bytesRead);
                copyStream.Position = 0;

                return copyStream;
            }
        }
    }
}
